package pig

import (
	"context"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/gorilla-go/container"
	"github.com/gorilla-go/pig/constant"
)

// 路由
type IRouter interface {
	Route(*http.Request) (func(ctx context.Context), map[string]string)
}

// 中间件
type IMiddleware func(ctx context.Context, next func(ctx context.Context))

// 错误处理器
type IErrorHandler func(any)

// 服务
type Server struct {
	router       IRouter
	middlewares  []IMiddleware
	errorHandler IErrorHandler
}

// 创建服务
func NewServer() *Server {
	return &Server{
		router:       nil,
		middlewares:  []IMiddleware{},
		errorHandler: nil,
	}
}

// 设置路由
func (k *Server) Router(router IRouter) *Server {
	k.router = router
	return k
}

// 设置错误处理器
func (k *Server) SetErrorHandler(errorHandler IErrorHandler) *Server {
	k.errorHandler = errorHandler
	return k
}

// 使用中间件
func (k *Server) Use(middleware ...IMiddleware) *Server {
	k.middlewares = append(k.middlewares, middleware...)
	return k
}

// 启动服务
func (k *Server) Run(port string) {
	// 检查路由
	if k.router == nil {
		panic("please set router first.")
	}

	// 创建根路由
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// 捕获异常
		defer func() {
			if k.errorHandler == nil {
				return
			}

			if err := recover(); err != nil {
				k.errorHandler(err)
			}
		}()

		// 获取控制器
		controller, params := k.router.Route(r)
		if controller == nil {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		// 创建容器
		ctx := context.WithValue(
			context.Background(),
			constant.RouterParams,
			params,
		)
		c := container.NewContainer()
		// 设置容器
		ctx = context.WithValue(ctx, constant.Container, c)

		// 设置请求
		container.SetSingletonWithAlias(c, constant.Request, r)
		// 设置响应
		container.BindImplement[http.ResponseWriter](c, w)

		// 执行管道
		pipeline := newPipeline[context.Context]()
		pipeline.Send(ctx)
		for _, middleware := range k.middlewares {
			pipeline.Through(middleware)
		}

		// 执行控制器
		pipeline.Then(controller)
	})

	// 创建服务
	server := &http.Server{
		Addr:    port,
		Handler: mux,
	}

	// 启动服务
	go func() {
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			panic(err)
		}
	}()

	// 监听关闭信号
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
	<-signalChan

	// 正常关闭
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()
	err := server.Shutdown(ctx)
	if err != nil && err != http.ErrServerClosed {
		if err == context.DeadlineExceeded {
			panic("server shutdown timeout.")
		}
		panic(err)
	}

	// 退出
	os.Exit(0)
}
